perm filename ALAID.PAL[AL,HE]9 blob
sn#368752 filedate 1978-07-20 generic text, type C, neo UTF8
COMMENT ⊗ VALID 00021 PAGES
C REC PAGE DESCRIPTION
C00001 00001
C00003 00002 FILES, SETNAM
C00005 00003 Data structures: Notes, note cells, message buffers
C00009 00004 GETNOTE, SNDNOTE, SERVER
C00013 00005 DOGTBUF, DOUSBUF, DORLBUF
C00015 00006 LINKQUEUE, UNLQUE, SAMEID
C00018 00007 TREATMESSAGE, GETOFS, DOERR, SNDANS
C00025 00008 MAKREQ, SNDREQ
C00029 00009 KTABLE, RTABLE, LOOKUP, RLOOKP, GETOCT, ascie messages
C00038 00010 TACK, SKIPSP, SKIPOPT
C00040 00011 DOGETVAL, DOSETVAL
C00049 00012 DOWAIT, DOSIGNAL
C00054 00013 DOSETNAM
C00058 00014 DOSTART, DODDT, DONOTICE
C00064 00015 DOHALT, DOGO
C00067 00016 DOSHOW, ONESHOW, DOPUT, NUMRFY
C00077 00017 DOBREAK, DOUNBREAK, BRSRCH
C00083 00018 DOJUMP, DOSTEP, ONESTEP
C00087 00019
C00088 00020 Driver for test of communications, ALINIT, ALKILL
C00092 00021 BUGS
C00093 ENDMK
C⊗;
; FILES, SETNAM
.IFNDF ALAID
DEBUG == 1
.IFF
DEBUG == 0
.ENDC
KERNEL == 1
FLOAT == 1
.IFNZ DEBUG
;Set up the necessary mapping for the Zonker
.INSRT ZONKER.PAL[AL,HE]
.OFFSET -160000 ;Put ALAID in the Zonker
.IF1
.TITLE Test of ALAID
.INSRT ALHEAD.PAL[AL,HE]
.INSRT K1DEF.PAL[11,SYS]
.ENDC
. = PATCH
.BLKW 200 ;Patch area
;If DDT sends us to user I space this will start the Kernel up anyway
. = START
RESTRT ;EMT gets us into Kernel I space
RESTRT
RESTRT ;Kernel INIT entry point
. = INTRP
CODE$ == . ;Interpreter code & data spaces start here
DATA$ == .
.INSRT ALIO.PAL[AL,HE]
.INSRT FLOAT.PAL[AL,HE]
STSW LBDEBUG,1 ;1 => first word of any large block is address of maker.
.INSRT LARGEB.PAL[AL,HE]
INSTSZ == 20 ;Size of an interpreter stack
.ENDC
.IFZ DEBUG
CODE
; Special pseudo-ops
SETNAM: ;Interpreter code
MOV @IPC(R4),INTNAM(R4)
BMPIPC ;
CCC ;Clear Condition Code
RTS PC ;Done
.ENDC
; Data structures: Notes, note cells, message buffers
; Notes from 10 to 11:
GETBUF == 1 ;
USEBUF == 2 ;
RELBUF == 3 ;
; Notes from 11 to 10:
BUFALC == 101 ;
TAKBUF == 102 ;
; Offsets in notes:
ARG1 == 2
ARG2 == 4
; Offsets in message buffers:
MESID == 0 ;
MESTYP == 2 ;
FROMTEN == 1 ;
FROMELF == 2 ;
REQUEST == 4 ;
ANSWER == 10 ;
MESLTH == 4 ;
MESBEG == 6 ;
;NOTB10 The notebox from 11 to the 10 (byte address) defined in COMTAB
;NOTB11 The notebox from 10 to the 11 (byte address) defined in COMTAB
NOTSIZ == 3 ; In WORDS!
BUFSIZ == 200 ; In WORDS!
DATA
NXTID: .WORD 0 ;Always even
CURNAM: .WORD 0 ;The current ISB for active interpreter.
ALLIVE: .WORD 0 ;AL interpreter alive if non-zero
; Answer block:
II == 0
XX ANSBUF ;Points to a buffer for the return answer
XX ANPTR ;Initialized to point to the start of the message in ANSBUF
XX AGBUF ;Start of the request buffer
XX AGARG ;Start of the arguments in request buffer
XX AGPTR ;Points to the current place in the request
XX VALPTR ;The value to be used in the answer
XX GPHPTR ;The graph node to be used in the answer
ABKSIZ == II/2 ;Size of an answer block, in words.
; Request block:
II == 0
XX REQBUF ;Place where the request will be assembled
XX REQPTR ;Current end of the assembled request
XX REQRES ;Where the response is placed
XX REQEVT ;The event that will signal the return of the response
XX REQQUE ;The queue node holding our waiting process
RQBSIZ == II/2 ;Size in WORDS.
; Interlock event
ALDEVT: .WORD 0
;HN Halt Switch
HALTSW: .WORD 0 ;HN 0 = Run , 1 = Halt
; Waitqueue structure:
II == 0
XX QNEXT ;Next entry on queue
XX QPREV ;Previous entry on queue
QID == II ;Identifier of this node. Same field as QEVT.
XX QEVT ;The event this waiter is expecting
XX QBUF ;The answer he was waiting for
QUELTH == II/2 ;Length of queue node in WORDS.
WAITQ: .BLKW QUELTH ;List of processes waiting to hear answers.
CODE
; GETNOTE, SNDNOTE, SERVER
COMMENT ⊗ Since there is only one server, it is not necessary to put
any interlocks around code in GETNOTE and SNDNOTE. ⊗
GETNOTE:
COMMENT ⊗ Returns the first note seen in a block pointed to by R0. ⊗
MOV R2,-(SP) ;Save R2
1$: TST NOTB11 ;Anything there?
BNE 2$ ;Yes
SLEEP #100 ;and sleep a while
TST ALLIVE ;See if the main interpreter has gone away
BNE 1$ ;if not try again
DISMIS ;if so we should die
2$: MOV #NOTSIZ,R0 ;
MOV R0,R2 ;R2 ← Count of how many words to transfer
JSR PC,GTFREE ;R0 ← place to store the note
MOV #NOTB11,R1 ;Transfer the note
3$: MOV (R1)+,(R0)+
SOB R2,3$ ;Repeat
SUB #2*NOTSIZ,R0 ;Reset R0 to point to front of note.
CLR NOTB11 ;Clear the note, to say we got it.
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
SNDNOTE:
COMMENT ⊗ R0 points to a note to send. Send it and then release the
block. ⊗
MOV R2,-(SP) ;Sve R2
1$: TST NOTB10 ;Anything there?
BEQ 2$ ;No.
SLEEP #100 ;Yes, so sleep a while
BR 1$ ;And try again
2$: MOV #NOTSIZ-1,R1 ;R1 ← count of words to send
MOV #NOTB10+2,R2;R2 ← Where to put it.
TST (R0)+ ;Skip the first word; we will put it in last
3$: MOV (R0)+,(R2)+
SOB R1,3$ ;Repeat
SUB #2*NOTSIZ,R0 ;Reset R0 ← LOC[note]
MOV (R0),NOTB10 ;Activate the note by sending the first word
JSR PC,RLFREE ;Release the block.
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
SERVER:
COMMENT ⊗ Listens for notes from the 10 and acts on them. Never
returns. Uses R2. ⊗
JSR PC,GETNOTE ;R0 ← LOC[note]
MOV (R0),R1 ;R1 ← type of note
MOV R0,R2 ;R2 ← LOC[note]
CMP R1,#GETBUF ;GETBUF
BNE 1$
JSR PC,DOGTBUF ;
BR 4$ ;
1$:
CMP R1,#USEBUF ;USEBUF
BNE 2$
JSR PC,DOUSBUF ;
BR 4$ ;
2$:
CMP R1,#RELBUF ;RELBUF
BNE 3$
JSR PC,DORLBUF ;
BR 4$ ;
3$:
ALERR SRVMES ;Illegal code
4$: MOV R2,R0 ;Release the note.
JSR PC,RLFREE ;
BR SERVER ;One more river, there's one more river to cross.
DATA
SRVMES: ASCIE </CAN'T UNDERSTAND NOTE FROM THE 10/>
CODE
; DOGTBUF, DOUSBUF, DORLBUF
DOGTBUF:
COMMENT ⊗ Called by SERVER. The 10 wants us to allocate a buffer.
R0 = LOC[note]. The size in bytes is in ARG1(R0). We should respond
with BUFALC <size> <adr>. ⊗
MOV ARG1(R0),R0 ;R0 ← size argument
MOV R0,-(SP) ;Save size argument
JSR PC,GTFREE ;Get the buffer out of free storage
MOV R0,-(SP) ;Save buffer address
MOV #NOTSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new note to send]
MOV #BUFALC,(R0) ;BUFALC
MOV (SP)+,ARG2(R0) ; <adr>
MOV (SP)+,ARG1(R0) ; <size>
JSR PC,SNDNOTE ;Send the note off. (He will destroy it)
RTS PC ;Done
DOUSBUF:
COMMENT ⊗ Called by SERVER. R0 = LOC[note]. The buffer that starts
at address ARG1(R0) is a message. Look at it, act on it, and then
recycle the message buffer. ⊗
MOV ARG1(R0),R0 ;R0 ← LOC[message]
JSR PC,TREATMESSAGE ;Treat it and release it
RTS PC ;Done
DORLBUF:
COMMENT ⊗ Called by SERVER. R0 = LOC[note]. The buffer that starts
at ARG1(R0) has been used by the 10, and we may deallocate it now. ⊗
MOV ARG1(R0),R0 ;R0 ← LOC[expended message]
JSR PC,RLFREE ;
RTS PC ;Done
; LINKQUEUE, UNLQUE, SAMEID
LINKQUEUE:
COMMENT ⊗ There is a dummy queue at the start of the chain. R1
points to the queue header, and R0 is the one we wish to add in.
Exclusion should be on before this routine is called; it remains
on afterwards. ⊗
MOV QNEXT(R1),QNEXT(R0)
MOV R1,QPREV(R0)
MOV R0,QNEXT(R1)
RTS PC
UNLQUE:
COMMENT ⊗ R0 points to a queue node. It is unlinked from its queue.
R0 is left pointing at the same node. Exclusion should be on before
this routine is called; it will remain on afterwards. ⊗
MOV QPREV(R0),R1 ;R1 ← prev(old)
MOV QNEXT(R0),QNEXT(R1) ;Transfer forward link.
MOV QNEXT(R0),R1 ;R1 ← next(old)
BEQ 1$ ;If any
MOV QPREV(R0),QPREV(R1) ;Transfer backward link.
1$: RTS PC ;Done.
SAMEID:
COMMENT ⊗ R0 = header of queue. R1 = ID to look for. If there is a
node in the queue with that ID, it is returned in R0. Otherwise, R0
← 0. Exclusion should be on before this routine is called; it will
remain on afterwards. ⊗
1$: MOV QNEXT(R0),R0 ;R0 ← next (real) node in queue
BEQ 2$ ;If any.
CMP QID(R0),R1 ;Match the ID?
BNE 1$ ;No. Try next one.
JSR PC,UNLQUE ;R0 ← same node, now unlinked.
2$: RTS PC ;Done
; TREATMESSAGE, GETOFS, DOERR, SNDANS
TREATMESSAGE:
COMMENT ⊗ R0 = LOC[buffer from the 10]. Print out its contents and
treat it. ⊗
MOV R2,-(SP) ;Save R2
MOV R0,R2 ;R2 ← LOC[buffer]
;print the message
.IFZ DEBUG
EVWAIT CSLEVT ;
.ENDC
MOV #CRLFX,R0 ;
JSR PC,TYPSTR ;
MOV R2,R0 ;
ADD #MESBEG,R0 ;R0 ← LOC[start of message itself]
JSR PC,TYPSTR ;Print it
.IFZ DEBUG
EVSIG CSLEVT ;
.ENDC
;see what kind of message it is
MOV R2,R0 ;
MOV MESTYP(R0),R1 ;R1 ← MESTYPE;
BIT #ANSWER,R1 ;An answer?
BEQ 2$ ;No
;got a response. See if anyone is waiting to hear it.
MOV MESID(R0),R1;R1 ← MESID
EVWAIT ALDEVT ;Enter critical section
MOV #WAITQ,R0 ;R0 ← head of wait.
JSR PC,SAMEID ;R0 ← queue node waiting for this MESID.
EVSIG ALDEVT ;End of critical section
TST R0 ;Was there a waiting process?
BNE 1$ ;Yes.
ALERR TRTMMS ;None found. A bug!
1$: MOV R2,QBUF(R0) ;Give him his result.
EVSIG QEVT(R0) ;Give him his signal
BR 3$ ;Prepare to leave
;got a question. Get someone to look at it.
2$: JSR PC,RLOOKP ;Start up a process to fulfill the request and
;delete the message
3$: MOV (SP)+,R2 ;Restore R2
RTS PC ;
DATA
TRTMMS: ASCIE </GOT UNEXPECTED ANSWER FROM THE 10./>
CODE
GETOFS:
COMMENT ⊗ R2 = LOC[answer block]. We want to see (OFFSET n). If we
do, we put LOC[graph node for n] in GPHPTR(R2); otherwise R0 ← 0. R2 is
still LOC[answer block], but ARGPTR is properly updated. ⊗
MOV AGARG(R2),R0;R0 ← LOC[argument string]
CMPB (R0)+,#'( ;A left paren?
BNE 1$ ;No.
JSR PC,LOOKUP ;R0 ← next thing on arg, R1 ← OFSCOD, we hope.
CMP R1,#OFSCOD ;Was it offset?
BNE 1$ ;No.
JSR PC,GETOCT ;R0 ← after the arg, R1 ← octal number found.
MOV R0,AGPTR(R2);Save arg. ptr
MOV R1,R0 ;R0 ← integer offset
MOV CURNAM,R4 ;R4 ← LOC[ISB of active interpreter]
JSR PC,GETARG ;R0 ← LOC[environment entry for variable]
MOV R0,GPHPTR(R2)
BEQ 1$ ;If anyone home. Else will return failure.
MOV AGPTR(R2),R0;
JSR PC,SKIPSP ;Skip spaces.
MOV #'),R1 ;
JSR PC,SKIPOP ;Skip the ), if it is there.
MOV R0,AGPTR(R2);
RTS PC ;
1$: CLR R0 ;Failure return
RTS PC ;
DOERR:
COMMENT ⊗ There has been an error in parsing some command. R2 =
LOC[answer block]. We will say "ERROR (message)". R2 will be left
with ANPTR fixed up. ⊗
MOV ANPTR(R2),R0;R0 ← answer pointer
MOV #ERRMES,R1 ;
JSR PC,TACK ;Tack on "ERROR "
MOV #LPAREN,R1 ;
JSR PC,TACK ;Tack on " ( "
MOV AGBUF(R2),R1;
ADD #MESBEG,R1 ;
JSR PC,TACK ;Tack on the original message
MOV #RPAREN,R1 ;
JSR PC,TACK ;Tack on " ) "
MOV R0,ANPTR(R2);
JMP SNDANS ;He will never return.
SNDANS:
COMMENT ⊗ R2 = LOC[answer block]. ANPTR(R2) = end of the message.
ANSBUF(R2) = front of the message. Compute the message length, send
the message out, reclaim the answer block, including the AGBUF, and
then reclaim the interpreter stack, the PDB of this process and
dismiss. ⊗
;compute MESLTH
MOV ANPTR(R2),R1;R1 ← ans. ptr
MOV ANSBUF(R2),R0 ;R0 ← LOC[answer buffer]
SUB R0,R1 ;R1 ← length in bytes of message
ASR R1 ;in words
BCC 1$ ;HN OK if even numbers of bytes
INC R1 ;HN otherwise add 1 to the length in words
INC ANPTR(R2) ;HN reflct on the ans. ptr
MOVB #' ,@ANPTR(R2) ;HN and put a blank byte at the end of the mess.
1$: MOV R1,MESLTH(R0); MESLTH
;send the result back. R0 = LOC[message]
MOV #NOTSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new note to send]
MOV #TAKBUF,(R0);TAKBUF
MOV (SP),R1 ;R1 ← LOC[answer block]
MOV ANSBUF(R2),ARG1(R0) ; <adr>
JSR PC,SNDNOTE ;Send the note off. (He will destroy it)
;reclaim answer block
MOV R2,R0 ;Reclaim the argument message buffer
MOV AGBUF(R0),R0;
JSR PC,RLFREE ;
MOV R2,R0 ;Reclaim the answer block itself
JSR PC,RLFREE ;
;reclaim interpreter stack
MOV R3,R0
SUB #2*INSTSZ,R0
JSR PC,RLFREE
;reclaim Processor Desriptor Block
MOV R5,R0 ;
JSR PC,RLFREE ;
DISMIS ;Gone!
; MAKREQ, SNDREQ
MAKREQ:
COMMENT ⊗ Returns in R3 a pointer to a brand new request block, with
REQBUF and REQPTR initialized to a new area for assembling a request.
The REQBUF is initialized with MESTYP. ⊗
MOV #RQBSIZ,R0 ;Get a request block
JSR PC,GTFREE ;
MOV R0,R3 ;R3 ← LOC[request block]
MOV #BUFSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[request buffer]
MOV #FROMELF+REQUEST,MESTYP(R0)
MOV R0,REQBUF(R3)
ADD #MESBEG,R0 ;
MOV R0,REQPTR(R3)
RTS PC ;
SNDREQ:
COMMENT ⊗ R3 = LOC[request block]. REQPTR(R3) = end of the message.
REQBUF(R3) = front of the message. Compute the message length, send
the message out, wait for a reply, and then put the response in
REQRES(R3). R3 is left pointing to the request block. ⊗
;compute MESLTH
MOV REQPTR(R3),R1 ;R1 ← ans. ptr
MOV REQBUF(R3),R0 ;R0 ← LOC[request buffer]
SUB R0,R1 ;R1 ← length in bytes of message
ASR R1 ;in words
MOV R1,MESLTH(R0); MESLTH
MOV REQBUF(R3),R0 ;R0 ← LOC[message buffer]
EVMAK ;Get an event that will signal the response to the request.
MOV (SP),MESID(R0) ;That will be the MESID.
MOV (SP)+,REQEVT(R3) ;REQEVT
MOV #QUELTH,R0 ;Enqueue ourselves for the response
JSR PC,GTFREE ;R0 ← LOC[queue node]
MOV R0,REQQUE(R3) ;REQQUE
MOV REQEVT(R3),QEVT(R0) ;QEVT
EVWAIT ALDEVT ;Enter critical region
MOV #WAITQ,R1 ;
JSR PC,LINKQUEUE;
EVSIG ALDEVT ;Leave critical region
;send the request out. R0 = LOC[message]
MOV #NOTSIZ,R0 ;
JSR PC,GTFREE ;R0 ← LOC[new note to send]
MOV #TAKBUF,(R0);TAKBUF
MOV REQBUF(R3),ARG1(R0) ; <adr>
JSR PC,SNDNOTE ;Send the note off. (He will destroy it)
EVWAIT REQEVT(R3) ;Wait for the event to happen
COMMENT ⊗ When the answer comes, the server will unlink the
queue for us. We must destroy the event and reclaim the
queue node ourselves. ⊗
;the response has come, and the answer is in QBUF(REQQUE(R3))
EVKIL REQEVT(R3) ;
MOV REQQUE(R3),R0 ;
MOV QBUF(R0),REQRES(R3) ;REQRES
JSR PC,RLFREE ;Release the queue node
RTS PC ;
; KTABLE, RTABLE, LOOKUP, RLOOKP, GETOCT, ascie messages
DATA
LPAREN: .ASCIZ / ( /
RPAREN: .ASCIZ / ) /
DONEMES:.ASCIZ /DONE /
ERRMES: .ASCIZ /ERROR /
YTHMES: .ASCIZ /YOUTHERE /
CRLFMS: .ASCIZ /
/ ;HN
HLTMSG: .ASCIZ /ALL ACTIVE INTERPRETERS HALTED/ ;HN
ADRMES: .ASCIZ /ADDRESS: / ;HN
PCDMES: .ASCIZ / P_CODE:/ ;HN
OVRMES: .ASCIZ /SORRY ... BREAK TABLE OVERFLOW/ ;HN
NOBRKPT:.ASCIZ /NO BREAKPOINT AT THAT ADDRESS/ ;HN
SAMEBR: .ASCIZ /THERE IS ALREADY A BREAKPOINT THERE !/ ;HN
SETHLT: .ASCIZ /PLEASE FIRST "HALT" / ;HN
ASTRSK: .ASCIZ /*/ ;HN
COMSPC: .ASCIZ /, / ;HN
.EVEN
.MACRO KWORD KNAME, KINFO
II == .
ASCIE /KNAME/
. = II + 6 ;Truncate to 6 characters
KINFO ;Either code for this keyword, or service routine address
.ENDM
OFSCOD == 1
SCACOD == 2
VCTCOD == 3
TRACOD == 4
PLCCOD == 5
KTABLE: ;List of keywords.
KWORD <OFFSET>, OFSCOD
KWORD <SCALAR>, SCACOD
KWORD <VECTOR>, VCTCOD
KWORD <TRANS >, TRACOD
KWORD <PLACE >, PLCCOD
KTEND: .WORD 0
RTABLE: ;List of requests.
KWORD <GETVAL>, DOGETVAL
KWORD <SETVAL>, DOSETVAL
KWORD <SIGNAL>, DOSIGNAL
KWORD <WAIT >, DOWAIT
KWORD <SETNAM>, DOSETNAM
KWORD <START >, DOSTART
KWORD <DDT >, DODDT
KWORD <NOTICE>, DONOTICE
KWORD <HALT >, DOHALT ;HN
KWORD <SHOW >, DOSHOW ;HN
KWORD <PUT >, DOPUT ;HN
KWORD <BREAK >, DOBREAK ;HN
KWORD <UNBRK >, DOUNBREAK ;HN
KWORD <JUMP >, DOJUMP ;HN
KWORD <GO >, DOGO ;HN
KWORD <STEP >, DOSTEP ;HN
RTEND: .WORD 0
CODE
COMMENT ⊗ R0 = LOC[string]. Find which keyword heads the string,
using a disgusting linear search, and return R1 ← 0 if no keyword
found, otherwise R1 ← code for that keyword. R0 ← next entry on
string. ⊗
LOOKUP:
MOV R2,-(SP) ;Save R2
MOV #KTABLE,R1 ;R1 ← LOC[current try in KTABLE]
1$: MOV #6,R2 ;R2 ← count of how many characters to match.
2$: CMPB (R0)+,(R1)+;Match the next letter?
BEQ 4$ ;Yes
3$: ADD R2,R0 ;
SUB #7,R0 ;R0 ← start of given string.
ADD R2,R1 ;R1 ← end of test string
TSTB (R1)+ ;R1 ← start of next test string
CMP R1,#KTEND ;Off the end?
BLO 1$ ;No.
BR 6$ ;Yes.
4$: SOB R2,2$ ;Try the next, if any.
;found a match. R1 = LOC[KINFO]
JSR PC,SKIPSP ;Skip spaces (does not hurt R1)
MOV (R1),R1 ;R1 ← KINFO
5$: MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
6$: CLR R1 ;Did not find anything
BR 5$ ;
COMMENT ⊗ R0 = LOC[message buffer request]. Find which request word
heads the string, using a disgusting linear search, and start a
process to handle the request. He will see to the deletion of the
message buffer. ⊗
RLOOKP:
MOV R2,-(SP) ;Save R2
MOV R0,-(SP) ;Save LOC[message buffer request]
ADD #MESBEG,R0 ;R0 ← LOC[request string]
MOV #RTABLE,R1 ;R1 ← LOC[current try in KTABLE]
1$: MOV #6,R2 ;R2 ← count of how many characters to match.
2$: CMPB (R0)+,(R1)+;Match the next letter?
BEQ 4$ ;Yes
3$: ADD R2,R0 ;
SUB #7,R0 ;R0 ← start of given string.
ADD R2,R1 ;R1 ← end of test string
TSTB (R1)+ ;R1 ← start of next test string
CMP R1,#RTEND ;Off the end?
BLO 1$ ;No.
MOV #DOERR,R2 ;So what we will do is handle the error.
BR 5$
4$: SOB R2,2$ ;Try the next, if any.
;found a match. R1 = LOC[KINFO]
MOV (R1),R2 ;R2 ← KINFO = address of service routine
5$: JSR PC,SKIPSP ;Skip spaces
MOV R0,-(SP) ;Save AGPTR
;build the answer block
MOV #BUFSIZ,R0
JSR PC,GTFREE ;R0 ← LOC[answer buffer]
MOV 2(SP),R1 ;R1 ← AGBUF
MOV MESID(R1),MESID(R0) ;Transfer the MESID to answer from request.
MOV #FROMELF+ANSWER,MESTYP(R0) ;MESTYP
MOV R0,-(SP) ;Save ANSBUF
MOV #ABKSIZ,R0 ;Get an answer block
JSR PC,GTFREE ;R0 ← LOC[answer block]
MOV (SP)+,R1 ;R1 ← ANSBUF
MOV R1,ANSBUF(R0)
ADD #MESBEG,R1 ;
MOV R1,ANPTR(R0);
MOV (SP),AGARG(R0)
MOV (SP)+,AGPTR(R0)
MOV (SP)+,AGBUF(R0)
MOV R0,-(SP) ;Save LOC[answer block]
;set up a new process with R2 ← LOC[answer block] to fulfil request.
INSTSZ == 20 ;Size of an interpreter stack
MOV #INSTSZ,R0 ;R3 stack space
JSR PC,GTFREE ;
ADD #2*INSTSZ,R0 ;to end of space
MOV R0,-(SP) ;Save stack space
MOV #210,R0 ;Room for process descriptor
JSR PC,GTFREE ;R0 ← LOC[new process descriptor]
MOV #UFPUSE+UGRSAV+4,PDBSTA(R0);Use floating point, use saved registers.
MOV R0,USKMIN(R0) ;Set up min pointer for SP
ADD #UFEC+36,USKMIN(R0)
MOV R0,USKMAX(R0) ;Set up max pointer for SP
ADD #420,USKMAX(R0)
MOV #144100,UPSW(R0) ;Set up psw
MOV (SP)+,PDBR3(R0) ;Store away the R3 stack pointer.
MOV (SP)+,PDBR2(R0) ;Store away the R2 = LOC[answer block]
MOV CURNAM,PDBR4(R0) ;Start out on the current ISB
MOV R0,PDBR5(R0) ;Store away the R5 = PDB address.
MOV #USRIM,UIMAP(R0) ;Map instruction space
FORK R0,R2,#USRDM ;Cause the new process to be started
6$: MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
GETOCT:
COMMENT ⊗ R0 = string pointer. Finds an octal number, skipping
spaces to do so, and places its value in R1. Leaves R0 at end of
spaces following the string. ⊗
MOV R2,-(SP) ;Save R2
CLR R1 ;R1 is the eventual result
JSR PC,SKIPSP ;Skip leading spaces
1$: MOVB (R0)+,R2 ;R2 ← Character
CMP #'0,R2 ;Too small?
BGT 2$ ;yes
CMP #'7,R2 ;Too large?
BGE 3$ ;no
2$: TSTB -(R0) ;Move back one place
JSR PC,SKIPSP ;skip trailing spaces
MOV (SP)+,R2 ;Restore R2
RTS PC ;Done
3$: MOV R2,-(SP) ;Save the character
ASH #3,R1 ;Compute new result
BIC #60,(SP) ;
ADD (SP)+,R1 ;
BR 1$ ;And repeat
TACK, SKIPSP, SKIPOPT
TACK:
COMMENT ⊗ R1 = LOC[ascie string to tack on], R0 = LOC[where to put
it]. Returns R0 ← next location available in destination string. ⊗
MOVB (R1)+,(R0)+;Copy a byte
BNE TACK ;Repeat while necessary
DEC R0 ;Go back past the null
RTS PC ;Done
SKIPSP:
COMMENT ⊗ R0 = LOC[string]. Skip past any spaces, returning R0 ← LOC[next
non-space element of the string. Leaves R1 unchanged. ⊗
CMPB (R0)+,#' ;
BEQ SKIPSP ;
DEC R0 ;Go back past the non-space
RTS PC ;
SKIPOPT:
COMMENT ⊗ R0 = LOC[string]. Skip past the character in R1, if it is
the next character, and in any case, skip past any spaces. ⊗
CMPB (R0),R1 ;The optional character?
BNE 1$ ;No
TSTB (R0)+ ;Yes. Skip it.
1$: JMP SKIPSP ;Skip over spaces, and let SKIPSP return.
; DOGETVAL, DOSETVAL
COMMENT ⊗ All service routines are instantiated as processes, where
R2 points at an answer block, with ANPTR, ANSBUF, AGBUF, AGPTR, AGARG
all set up. The ANSBUF already has MESID and MESTYP set. R3 points
at an interpreter stack, if it should be needed, and R5 points at the
PDB, for reclamation purposes. Service routines dismiss when they
are finished, having destroyed their PDB. ⊗
DOGETVAL: ;Service routine
COMMENT ⊗ Currently accepted argument string is: (OFFSET n). The
OFFSET construct will cause that variable in the current interpreter
to have its value produced. The answer is of the form "ISVAL arg
val", unless something goes wrong, in which case the answer will be
"ERROR (GETVAL arg)". ⊗
;scan the arguments
JSR PC,GETOFS ;GPHPTR(R2) ← LOC[environment entry for offset]
TST R0 ;or was there an error?
BEQ 3$ ;oops.
MOV GPHPTR(R2),R0 ;R0 ← LOC[Env entry]
TSTB 1(R0) ;Check accessing method
BNE 1$
MOV 2(R0),VALPTR(R2) ;Direct access - store away LOC[value]
BR 2$
1$: CALL GETVAL,<2(R0)> ;Indirect access - R0 ← LOC[value]
MOV R0,VALPTR(R2) ;
2$: MOV AGPTR(R2),R0;
TSTB (R0) ;At the end?
BNE 3$ ;No. extra arguments.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV VALPTR(R2),R1 ;R0 ← LOC[value]
JSR PC,TACKVAL ;Tack it on
MOV R0,ANPTR(R2);
BR 4$ ;Ready to send it back
;In this case, cannot make sense of the argument.
3$: JMP DOERR ;
4$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
DOSETVAL:
COMMENT ⊗ Currently accepted argument string is: (OFFSET n) (SCALAR
n.n), (VECTOR n n n), or (TRANS n n ... n). The variable specified
by the first argument has its value changed to the value given by the
second argument. The answer is of the form "DONE", unless something
goes wrong, in which case the answer will be "ERROR (SETVAL args)".
⊗
;scan the arguments
JSR PC,GETOFS ;GPHPTR(R2) ← LOC[environment entry for offset]
TST R0 ;or was there an error?
BEQ 7$ ;oops.
CMPB (R0)+,#'( ;A left paren?
BNE 7$ ;No.
JSR PC,LOOKUP ;R0 ← next thing on arg, R1 ← SCLCOD, we hope.
CMP R1,#SCACOD ;Was it SCALAR?
BNE 1$ ;No.
JSR PC,RELSCN ;R0 ← after the arg, R1 ← 0 <=> number, AC0 ← float rep.
MOV R0,AGPTR(R2);Save arg. ptr
TST R1 ;Number?
BNE 7$ ;No
JSR PC,GETSCA ;R0 ← -(R3) ← LOC[scalar cell]
MOV (R3)+,VALPTR(R2)
STF AC0,(R0) ;Put 'er in.
BR 5$
1$: CMP R1,#VCTCOD ;Was it VECTOR?
BNE 3$ ;No.
MOV R0,AGPTR(R2);Save arg. ptr
JSR PC,GETVEC ;R0 ← -(R3) ← LOC[trans cell]
MOV (R3)+,VALPTR(R2)
MOV R3,-(SP) ;Save R3
MOV R4,-(SP) ;Save R4
MOV R0,R4 ;R4 ← LOC[vector cell]
MOV #3,R3 ;R3 ← count of how many places in VECTOR to fill.
MOV AGPTR(R2),R0;
2$: JSR PC,RELSCN ;R0 ← after the arg, R1 ← 0 <=> number, AC0 ← float rep.
TST R1 ;Number?
BNE 6$ ;No
STF AC0,(R4)+ ;Put 'er in.
SOB R3,2$ ;Repeat
MOV ONE,(R4)+ ;Set weight to one
MOV R0,AGPTR(R2);
MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
BR 5$
3$: CMP R1,#TRACOD ;Was it TRANS?
BNE 7$ ;No.
MOV R0,AGPTR(R2);Save arg. ptr
JSR PC,GETTRN ;R0 ← -(R3) ← LOC[trans cell]
MOV (R3)+,VALPTR(R2)
MOV R3,-(SP) ;Save R3
MOV R4,-(SP) ;Save R4
MOV R0,R4 ;R4 ← LOC[trans cell]
MOV #14,R3 ;R3 ← count of how many places in TRANS to fill.
MOV AGPTR(R2),R0;
4$: JSR PC,RELSCN ;R0 ← after the arg, R1 ← 0 <=> number, AC0 ← float rep.
TST R1 ;Number?
BNE 6$ ;No
STF AC0,(R4)+ ;Put 'er in.
SOB R3,4$ ;Repeat
MOV R0,AGPTR(R2);
MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
5$: MOV GPHPTR(R2),R1 ;R1 ← LOC[environment entry]
TSTB 1(R1) ;Check if direct access
BNE 10$ ; nope
MOV VALPTR(R2),2(R1) ; Yes - store value pointer in environment
BR 11$
10$: CALL CHANGE,<2(R1),VALPTR(R2)>
11$: MOV AGPTR(R2),R0;R0 ← arg. ptr.
JSR PC,SKIPSP ;Scan past spaces
MOVB #'),R1 ;
JSR PC,SKIPOPT ;Skip right paren, if any, plus spaces
TSTB (R0) ;At the end?
BNE 7$ ;No. extra arguments.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
MOV R0,ANPTR(R2);
BR 8$ ;Ready to send it back
;in this case, trying to scan a number and failed.
6$: MOV (SP)+,R4 ;Restore R4
MOV (SP)+,R3 ;Restore R3
;In this case, cannot make sense of the argument.
7$: JMP DOERR ;
8$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
; DOWAIT, DOSIGNAL;
DOSIGNAL: ;Service routine
COMMENT ⊗ Currently accepted argument string is: (OFFSET n). The
OFFSET construct will cause that variable in the current interpreter
to be signaled. The answer is of the form "DONE", unless something
goes wrong, in which case the answer will be "ERROR (SIGNAL arg)". ⊗
;scan the arguments
JSR PC,GETOFS ;GPHPTR(R2) ← LOC[environment entry for event]
TST R0 ;or was there an error?
BEQ 1$ ;oops.
MOV GPHPTR(R2),R0
EVSIG 2(R0) ;Signal the event.
MOV AGPTR(R2),R0;
TSTB (R0) ;At the end?
BNE 1$ ;No. extra arguments.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
MOV R0,ANPTR(R2);
BR 2$ ;Ready to send it back.
;In this case, cannot make sense of the argument.
1$: JMP DOERR ;
2$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
DOWAIT: ;Service routine
COMMENT ⊗ Currently accepted argument string is: (OFFSET n). The
OFFSET construct will cause that variable in the current interpreter
to be waited. The answer is of the form "DONE" when the wait is up,
unless something goes wrong, in which case the answer will be "ERROR
(WAIT arg)". ⊗
;scan the arguments
JSR PC,GETOFS ;GPHPTR(R2) ← LOC[environment entry for event]
TST R0 ;or was there an error?
BEQ 1$ ;oops.
MOV GPHPTR(R2),R0
EVWAIT 2(R0) ;WAIT for the event.
MOV AGPTR(R2),R0;
TSTB (R0) ;At the end?
BNE 1$ ;No. extra arguments.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
MOV R0,ANPTR(R2);
BR 2$ ;Ready to send it back.
;In this case, cannot make sense of the argument.
1$: JMP DOERR ;
2$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
; DOSETNAM
DOSETNAM: ;Service routine
COMMENT ⊗ Currently accepted argument string is: "n". the
interpreter with that name will be selected, and its ISB placed in
R4. The answer is of the form "DONE" when the wait is up, unless
something goes wrong, in which case the answer will be "ERROR (SETNAM
arg)". ⊗
;scan the arguments
MOV AGPTR(R2),R0;
JSR PC,GETOCT ;R0 ← after the arg, R1 ← octal number seen
MOV R0,AGPTR(R2);Save arg. ptr
MOV R1,-(SP) ;Stack interpreter name
EVWAIT INTEVT ;Enter critical section
MOV #ISTBLK,R0 ;Find the right interpreter.
1$: MOV R0,R1 ;
MOV NXTINT(R1),R0;
BEQ 2$ ;No such interpreter.
CMP INTNAM(R0),(SP) ;Have we found ours yet?
BNE 1$ ;No. Try again.
EVSIG INTEVT ;End of critical section
TST (SP)+ ;Get rid of the interpreter name.
MOV R0,CURNAM ;CURNAM ← ISB of new interpreter
MOV AGPTR(R2),R0;
TSTB (R0) ;At the end?
BNE 3$ ;No. extra arguments.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
BR 4$ ;Ready to send it back.
;No such interpreter
2$: EVSIG INTEVT ;End of critical secton
;In this case, cannot make sense of the argument.
3$: JMP DOERR ;
4$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
; DOSTART, DODDT, DONOTICE
DOSTART: ;Service routine
COMMENT ⊗ Currently accepted argument string is: (PLACE n), which is
optional. A new interpreter is started up, either at n or at PCODE,
if the argument is missing. This new interpreter becomes the
selected interpreter. The answer is of the form "DONE", unless
something goes wrong, in which case the answer will be "ERROR (START
arg)". ⊗
;scan the arguments
MOV AGPTR(R2),R0;
TSTB (R0)+,#'( ;An argument?
BEQ 1$
JSR PC,LOOKUP ;
CMP R1,#PLCCOD ;A place?
BNE 3$ ;No. Illegal argument
JSR PC,GETOCT ;R0 ← after the arg, R1 ← number seen.
MOV R0,AGPTR(R2);Save arg. ptr
MOV R1,R0 ;R0 ← interpreter start address
BR 2$
1$: MOV #PCODE,R0 ;R0 ← interpreter start address
2$: CLR R1 ;No particular event when he is finished.
JSR PC,SPAWN ;R0 ← PDB[new interpreter process].
MOV PDBR4(R0),CURNAM ;Set current interpreter to this one.
CLR HALTSW ;HN Make sure the Halt Switch is turned off !
SCHEDU R0,#INTERP,#USRDM,#2 ;Cause the new process to be started, suspended
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
MOV R0,ANPTR(R2);
BR 4$ ;Ready to send it back.
;In this case, cannot make sense of the argument.
3$: JMP DOERR
4$: ;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
DODDT: ;Service routine
COMMENT ⊗ Jump to DDT, so that ↑P will proceed. The answer is of the
form "DONE", unless something goes wrong, in which case the answer
will be "ERROR (DDT arg)". ⊗
ALERR DODDTMES ;Here we go to DDT.
;test stuff. Current test: Try the turn-around question YOUTHERE
;at the ten.
MOV R3,-(SP) ;Save R3
JSR PC,MAKREQ ;R3 ← request block.
MOV REQPTR(R3),R0 ;R0 ← REQPTR
MOV #YTHMES,R1 ;Tack on "YOUTHERE"
JSR PC,TACK ;
MOV R0,REQPTR(R3)
JSR PC,SNDREQ ;Send the request on its way, and eventually come back
;with response in the REQRES(R3)
MOV REQRES(R3),R0 ;
ADD #MESBEG,R0 ;Print out the response
JSR PC,TYPSTR ;
MOV REQRES(R3),R0 ;Reclaim the response buffer
JSR PC,RLFREE ;
MOV R3,R0 ;Reclaim request block
JSR PC,RLFREE ;
MOV (SP)+,R3 ;Restore R3
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0 ;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
DATA
DODDTMES: ASCIE </SWITCHING TO DDT/>
CODE
DONOTICE: ;Service routine
COMMENT ⊗ The assumption is that someone has moved the arm. Call
MOVED to invalidate all devices and cause good values to be
generated. Return a response of the form "DONE", unless something
goes wrong, in which case the answer will be "ERROR (NOTICE)", which
really ought not to happen. ⊗
JSR PC,NOTICE ;Do the updating.
;prepare the answer. Note that TACK and TACKVAL take a string pointer
; in R0 and leave it right afterwords.
MOV ANPTR(R2),R0;R0 ← answer pointer
MOV #DONEMES,R1 ;
JSR PC,TACK ;Tack on "DONE "
;ANPTR(R2) = end of the message. ANSBUF(R2) = front of the message
JMP SNDANS ;Send it winging on its way. Reclaim the answer block.
;Reclaim the PDB. Dismiss.
; DOHALT, DOGO
DOHALT: ;HN
COMMENT ⊗ Test service routine for HALT.
Announce the HALT event for the interpreters and the user. ⊗
MOV #1,HALTSW ;HN Set the halt switch
MOV ANPTR(R2),R0 ;HN R0 ← answer pointer
MOV #HLTMSG,R1 ;HN message to the user
JSR PC,TACK ;HN Tack on "ALL INTERPRETERS HALTED"
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN send it,reclaim as RF says ..., and dismis.
DOGO:
COMMENT ⊗ Test service routine
Causes all WAKEVT's to be signalled. ⊗
MOV CURNAM,R4 ;HN
MOV IPC(R4),R1 ;HN
CMP (R1),#XBRACE;HN
BNE 11$ ;HN
JSR PC,BRSRCH ;HN Search the breakpoint table
TST R0 ;HN Found ?
BEQ 5$ ;HN No.
ADD #2,R0 ;HN Point to the pseudo OP-code
MOV (R0),(R1) ;HN Temporarily restore the p-code
JSR PC,ONESTEP ;HN And execute it
MOV #XBRACE,(R1);HN And the breakpoint (p-code)
BR 11$ ;HN Continue running from the next instruction ...
5$: ADD #2,IPC(R4) ;HN Search failure case; assuming compiler generarted brkpnt
11$: CLR HALTSW ;HN Make sure Halt Switch is turned off.
EVWAIT INTEVT ;HN Enter critical section
MOV #ISTBLK,R0 ;HN Initialize the link to all ISB's
1$: MOV NXTINT(R0),R0 ;HN R0 ← NEXT ISB
BEQ 2$ ;HN No more ?
EVSIG WAKEVT(R0);HN Signal this interpreter's WAKEVT
BR 1$ ;HN Go find next one
2$: EVSIG INTEVT ;HN End of critical section
; EVWAIT WAKEVT(R4);HN ******* JUST FOR TEST ***
MOV ANPTR(R2),R0;HN Prepare to send the DONE message and leave
MOV #DONEMES,R1 ;HN
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN
; DOSHOW, ONESHOW, DOPUT, NUMRFY
;Argument list generator macro :
;-------------------------------
.MACRO MAKEOP CNAME, ANAME, ARGLEN ;Compiler name, Address name
TEMP=. ;HN
.=ARGTBL+II ;HN Address of an entry in the argument ID table
.IFLE TEMP-.
.ERROR ARGTBL RUNS OUT OF ITS LIMIT
.ENDC
ARGLEN ;HN Argument list length ID (an entry)
.=TEMP ;HN
II=II+2
.ENDM
;Table of argument list lengths of all op_codes (7≡ ends with a list of arguments)
; (9≡ / / / / ... PLUS TWO MORE !!)
;Used by debugging routines
DATA
ARGTBL: .BLKW 250 ;HN
;The interpreter operation table
II=0
MAKEOP XINVALID,INVALID,INVALID ;Illegal instruction
.INSRT INTARG.PAL
CODE
DOSHOW:
COMMENT ⊗ Test service routine,
causes the current pseudo-code and its relative address (starting from 0)
to be shown to the requester. ⊗
MOV AGPTR(R2),R0;HN See how many pcodes the user wants to see
JSR PC,GETOCT ;HN put that number in R1
MOV CURNAM,R4 ;HN Look at the current interpreter
MOV IPC(R4),-(SP);HN Save current IPC
1$: JSR PC,ONESHOW ;HN Show one (next) pcode with its arguments (if any)
MOV R1,-(SP) ;HN Save the counter
MOV ANPTR(R2),R0;HN
MOV #CRLFMS,R1 ;HN
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
MOV NEWIPC,IPC(R4);HN IPC of the next pseudo instruction
MOV (SP)+,R1 ;HN Restore the counter
SOB R1,1$ ;HN Repeat above for as many times as wanted
MOV (SP)+,IPC(R4);HN Restore original IPC
JMP SNDANS ;HN
ONESHOW:
MOV R1,-(SP) ;HN Save R1 for the outer loop i.e. DOSHOW
MOV IPC(R4),-(SP);HN .. IPC
MOV ANPTR(R2),R0;HN Prepare the answer
MOV #ADRMES,R1 ;HN With "ADDRESS "
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN Update the answer pointer
MOV IPC(R4),R0 ;HN Take the current pseudo-code address
JSR PC,NUMRFY ;HN Numerify it (R1←string ptr.)
MOV ANPTR(R2),R0;HN &
JSR PC,TACK ;HN put it to be sent ...
MOV #PCDMES,R1 ;HN "P-CODE: "
JSR PC,TACK ;HN in the answer
MOV R0,ANPTR(R2);HN Update the answer pointer
MOV @IPC(R4),R0 ;HN Take the P-Code itself
CMP R0,#XBRACE ;HN Is there a breakpoint ?
BNE 1$ ;HN No. OK go ahead.
MOV IPC(R4),R1 ;HN Yes. Prepare to ...
JSR PC,BRSRCH ;HN Find the original pcode
TST R0 ;HN Was it a compiler generated brkpnt ?
BEQ 2$ ;HN Yes. Go ahead and show it as it is
ADD #2,R0 ;HN No. Point R0 to the pcode
MOV (R0),-(SP) ;HN Push the pcode
MOV ANPTR(R2),R0;HN
MOV #ASTRSK,R1 ;HN Put a *
JSR PC,TACK ;HN before the pcode
MOV R0,ANPTR(R2);HN in the show message
MOV (SP)+,R0 ;HN POP the pcode into R0
BR 1$ ;HN and continue ...
2$: MOV #XBRACE,R0 ;HN Do this when brkpnt is not in the BRKTBL
1$: MOV R3,-(SP) ;HN Save R3
tmp1: MOV ARGTBL(R0),R3 ;HN R3 ← No. of pcode arguments
MOV R3,ARGNUM ;HN no. of args.
JSR PC,NUMRFY ;HN Numerify the P_Code
MOV ANPTR(R2),R0;HN &
JSR PC,TACK ;HN put it to be sent ...
MOV R0,ANPTR(R2);HN Update the answer pointer
TST R3 ;HN Are there any arguments ?
BEQ 3$ ;HN No. just send the opcode
;HN Yes. prepare sending the arguments in the message
4$: ADD #2,IPC(R4) ;HN Point to the first argument in the pcode
MOV #COMSPC,R1 ;HN put comma and space before the next argument
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
MOV @IPC(R4),R0 ;HN Take the next argument
JSR PC,NUMRFY ;HN Numerify it
MOV ANPTR(R2),R0;HN &
JSR PC,TACK ;HN And put it to be sent
CMP ARGNUM,#7 ;HN Are we dealing with a list ?
BNE 5$ ;HN No.
TST @IPC(R4) ;HN Yes. End of the list ?
BNE 4$ ;HN No. Go back and take the next arg.
BR 6$ ;HN Yes. we are almost done
5$: SOB R3,4$ ;HN Go back to 4$ if any more arg.
6$: MOV R0,ANPTR(R2);HN All done. update the ANPTR
3$: MOV (SP)+,R3 ;HN Restore R3
ADD #2,IPC(R4) ;HN Update the new IPC
MOV IPC(R4),NEWIPC ;HN Save IPC pointer now for the outer loop (if any)
MOV (SP)+,IPC(R4);HN Restore IPC
MOV (SP)+,R1 ;HN Restore R1 (for the outer loop ...)
RTS PC ;HN And return to the calling routine
DATA
ARGNUM: .WORD 0 ;HN Number of arguments of a pcode
NEWIPC: .WORD 0 ;HN To be used for multiple shows (i.e. SHOW n) by DOSHOW
CODE
DOPUT:
COMMENT ⊗ Test service routine,
PUTs the given pcode (currently acceptable only in octal form) AT the given pcode
address. ⊗
MOV AGPTR(R2),R0;HN Look at the argument string
JSR PC,GETOCT ;HN R1← numeric value of the arg.
ADD #2,R0 ;HN AT ... (any two chars. the first being non-blank ..)
MOV R1,-(SP) ;HN save the code
JSR PC,GETOCT ;HN get the address
MOV (SP)+,(R1) ;HN PUT the code AT the address.
MOV ANPTR(R2),R0;HN
MOV #DONEMES,R1 ;HN DONE
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN
NUMRFY:
COMMENT ⊗ R0 contains the value to be octally numerified.
Octal string will be put in a buffer and will be pointed to by R1 (with one
null char. at the right end). ⊗
MOV R3,-(SP) ;HN save R3
MOV #NUMBUF+7,R3;HN initialize the string pointer
MOVB #0,(R3) ;HN push the null char.
DEC R3 ;HN
TST R0 ;HN is the number too large (looks negative ?)
BGE 3$ ;HN No. then enter the subroutine normally
JSR PC,4$ ;HN Yes then the subroutine, passed CMP test
BR 5$ ;HN
3$: JSR PC,1$ ;HN call internal (actual) numerifier.
5$: MOV R3,R1 ;HN R1 to be the string pointer
MOV (SP)+,R3 ;HN restore R3
RTS PC ;HN and return.
1$: CMP R0,BASE ;HN Is the argument less than the base ?
BLT 2$ ;HN yes, OK then it represents the list significant digit
4$: MOV R0,R1 ;HN No, PREPARE FOR DIVISION
CLR R0 ; //
DIV BASE,R0 ;HN Divide R0 by the BASE , R1 ← remainder
BISB #60,R1 ;HN ASCIIfy the remainder digit
MOVB R1,(R3) ;HN PUSH CURRENT least significant DIGIT
DEC R3 ;HN
BR 1$ ;HN Repeat for the next digit
2$: BISB #60,R0 ;HN ASCIIfy this digit
MOVB R0,(R3) ;HN PUSH least significant DIGIT
RTS PC
DATA
BASE: .WORD 10 ;HN
NUMBUF: .BLKB 10 ;HN string buffer
CODE
; DOBREAK, DOUNBREAK, BRSRCH
DOBREAK:
COMMENT ⊗ Test service routine.
Current acceptable argument is "n". Puts a break_point (XBRACE) in PCODE address "n"
and saves current op_code (along with its address/current IPC) in the break table. ⊗
MOV #BRKTBL,R0 ;HN Check for break table overflow
ADD #2*BRSIZE,R0;HN
CMP BRKPTR,R0 ;HN
BLT 1$ ;HN OK Go set the break-point
MOV ANPTR(R2),R0;HN OVERFLOW: Just tell the user about it
MOV #OVRMES,R1 ;HN
BR 2$ ;HN
1$: MOV AGPTR(R2),R0;HN Look at the argument string
JSR PC,GETOCT ;HN R1←numeric value of the argument
JSR PC,BRSRCH ;HN See if the brkpnk already there
TST R0 ;HN
BEQ 11$ ;HN
MOV #SAMEBR,R1 ;HN If true, tell the user about it
MOV ANPTR(R2),R0;HN
BR 2$ ;HN
11$: MOV R1,@BRKPTR ;HN Add the new IPC to the BRKTBL
ADD #2,BRKPTR ;HN Update the break table pointer
MOV (R1),@BRKPTR;HN Add the pseudo opcode in this IPC to the BRKTBL
ADD #2,BRKPTR ;HN Update the break table pointer
MOV #XBRACE,(R1);HN PUT THE BREAK-POINT IN THE PSEUDO CODE
MOV ANPTR(R2),R0;HN ................
MOV #DONEMES,R1 ;HN Answer will be "DONE"
2$: JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN Send it winging on its way ...
DOUNBREAK:
COMMENT ⊗ Test service routine.
Current acceptable argument is "n". Looks at P-CODE address "n" ; there should be
an XBRACE code there, if not nothing other than reporting of the subject to the
user occurs. If there was a break_point there, then the break(point) table gets
searched to see if the break_point was set by ALAID. If the search was successful
then the corresponding original op_code will be put back to the address "n" and
the break table gets compacted as we no longer need that entry in the table. If
the break_point had been put by the compiler, the search will probably fail in which
case the break_point will be cleared by putting an XNOOP code instead of the XBRACE
code at "n". ⊗
MOV AGPTR(R2),R0;HN Look at the argument string
JSR PC,GETOCT ;HN R1 ← numeric value of the argument
CMP (R1),#XBRACE;HN Is there a break_point there ?
BEQ 1$ ;HN Yes. Go do the job ..
MOV #NOBRKPT,R1 ;HN No. Report it to the user,
BR 7$ ;HN and leave.
1$: JSR PC,BRSRCH ;HN Search the breakpoint table
TST R0 ;HN Found ?
BEQ 5$ ;HN No.
CLR (R0)+ ;HN Yes. we found the entry. Clear the IPC field,
MOV (R0),(R1) ;HN put back the original op_code,
SUB #2,BRKPTR ;HN
MOV @BRKPTR,(R0);HN Put the last entry in the place of the previous brkpnt.
SUB #2,BRKPTR ;HN
MOV @BRKPTR,-(R0);HN
BR 6$ ;HN
5$: MOV #XNOOP,(R1) ;HN Search has failed, just put NOOP at "n"
;HN (It probably has been put by the compiler.)
6$: MOV #DONEMES,R1 ;HN Job is "DONE" ... let the user know
7$: MOV ANPTR(R2),R0;HN ....
JSR PC,TACK ;HN ....
MOV R0,ANPTR(R2);HN ....
JMP SNDANS ;HN ... And leave ...
BRSRCH:
Comment ⊗ Assuming R1=<pcode address>, this subroutine searches (linearly) the break
point table to find an entry with the same address field. R0 gets the found
address unless search fails in which case R0←0 .
⊗
1$: MOV #BRKTBL,R0 ;HN Initialize the table searching pointer
2$: CMP R0,BRKPTR ;HN
BGE 4$ ;HN Are we out of the table ?
CMP (R0),R1 ;HN No. Is this entry what we are looking for ?
BNE 3$ ;HN
RTS PC ;HN Yes. Return (R0 correct)
3$: ADD #4,R0 ;HN No. then go look at the next one.
BR 2$ ;HN
4$: CLR R0 ;HN Search failed: R0←0
RTS PC ;HN Return
BRSIZE = 100 ;HN Size of the break_table
DATA
BRKPTR: .WORD BRKTBL ;HN Break table pointer
BRKTBL: .BLKW BRSIZE ;HN Break table itself.
CODE
; DOJUMP, DOSTEP, ONESTEP
DOJUMP:
COMMENT ⊗ Test service routine.
Current acceptable argument is "n". Causes the current interpreter's IPC take the
value "n", as if it had executed an XJUMP code. ⊗
TST HALTSW ;HN Are interpreters in the HALT state ?
BNE 1$ ;HN Yes. OK go do the job.
MOV #SETHLT,R1 ;HN No. Then ask user to put them in that state
MOV ANPTR(R2),R0;HN
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN And leave ..
1$: MOV AGPTR(R2),R0;HN Look at "n"
JSR PC,GETOCT ;HN Take its value in R1
MOV CURNAM,R4 ;HN And the current interpreter in R4
MOV R1,IPC(R4) ;HN Actual jump !!
JMP DOGO ;HN And GO ...
DOSTEP:
COMMENT ⊗ Test service routine.
Causes the pseudo_code at current IPC of the current interpreter (pointed to by
CURNAM) to be executed. IPC will be updated accordingly.
This command takes no argument. ⊗
TST HALTSW ;HN Are interpreters in the HALT state ?
BNE 1$ ;HN Yes. OK go do the job.
MOV #SETHLT,R1 ;HN No. Then ask user to put them in that state
MOV ANPTR(R2),R0;HN
JSR PC,TACK ;HN
MOV R0,ANPTR(R2);HN
JMP SNDANS ;HN And leave ..
1$: MOV AGPTR(R2),R0;HN See how many steps the user wants to execute
JSR PC,GETOCT ;HN put that number in R1
2$: MOV R1,-(SP) ;HN Save the counter
MOV CURNAM,R4 ;HN Take the current interpreter
MOV IPC(R4),R1 ;HN and R1 to the current IPC
CMP (R1),#XBRACE;HN Is there a breakpoint at the current instruction
BNE 9$ ;HN No. OK proceed normally
JSR PC,BRSRCH ;HN Yes, there is a preakpoint there; search the brkpnt tbl.
TST R0 ;HN Search successful ?
BEQ 8$ ;HN No. Assume a compiler initiated breakpoint
ADD #2,R0 ;HN Yes. Point R0 to the pseudo OPcode
MOV (R0),(R1) ;HN And replace the XBRACE by that
JSR PC,ONESTEP ;HN Execute the pseudo instruction
MOV #XBRACE,(R1);HN Restore the breakpoint
BR 10$ ;HN Report
8$: ADD #2,IPC(R4) ;HN Pass the cmplr. init. brkpnt.
9$: JSR PC,ONESTEP ;HN Execute one pseudo instruction
10$: MOV (SP)+,R1 ;HN Restore the counter
SOB R1,2$ ;HN Repeat above for as many times as wanted
JSR PC,ONESHOW ;HN and SHOW the state of the world ..
JMP SNDANS ;HN
ONESTEP:
COMMENT ⊗ While HALTSW=1, calling this subroutine causes ONE pseudo instruction
to be executed.
⊗
MOV #ALDSS,DEBMOD(R4) ;HN Set the DEBugging MOde to Single Step
EVSIG WAKEVT(R4);HN Allow one pseudo-instruction to be executed
EVWAIT STPEVT(R4);HN Done ?
CLR DEBMOD(R4) ;HN Yes. Reset the DEBug MOde
RTS PC ;HN And return
; Driver for test of communications, ALINIT, ALKILL
.IFNZ DEBUG
temp == %OFFSE ;Save the current offset
.OFFSET 0 ;We want to use real physical addresses here for the kernel
PUTLOC JOBDAT, MAINBL
PUTLOC JOBSA, START
PUTLOC JOBDM, USRDM
.OFFSET temp ;Restore Offset
DATA
MAINBL: PDBLK 1,200,S ;Makes a process descriptor for main process
CODE
START: JSR PC,IOINIT ;
JSR PC,FRINIT ;
CLR NOTB10
CLR NOTB11
EVMAK ;Create and signal once the AL interlock event.
MOV (SP),ALDEVT ;
EVSIG ;
CLR WAITQ+QNEXT ;
JMP SERVER ;No, he'll never return
GETARG: MOV R0,FAKE ;
MOV #FAKE1,R0 ;
RTS PC
DATA
FAKE: .BLKW 2 ;Long enough for floating
FAKE1: FAKE
CODE
ROUTINE GETVAL,<GTV.ARG>
MOV GTV.ARG(RF),R0
RTS PC
ROUTINE CHANGE,<CHG.ND,CHG.VN>
RTS PC
GETSCA: MOV #FAKE,R0 ;
MOV R0,-(R3) ;
RTS PC ;
GETTRN: MOV #60,R0 ;
JSR PC,GTFREE ;
MOV R0,-(R3) ;
TACKVAL:
COMMENT ⊗ R1 = LOC[value], R0 ← where to put it ⊗
MOV #FAKEMES,R1 ;
JMP TACK ;
DATA
FAKEMES:ASCIE </999.999/>
CODE
.ENDC
DATA
ALPDB: PDBLK 2,150,S ;Makes a process descriptor for server
CODE
ALINIT:
COMMENT ⊗ Start up one copy of the server as a separate job. ⊗
EVMAK ;Create and signal once the AL interlock event.
MOV (SP),ALDEVT
EVSIG
CLR WAITQ+QNEXT
CLR NOTB11
CLR NOTB10
MOV #1,ALLIVE ;Indicate that the AL interpreter is alive
MOV #20,R0 ;R3 stack space
JSR PC,GTFREE
ADD #40,R0 ;to end of space
MOV #ALPDB,R1 ;R1 ← LOC[ALAID process descriptor]
BIS #UGRSAV+USKSAV,PDBSTA(R1) ;Use saved registers.
MOV R0,PDBR3(R1) ;Store away the R3 stack pointer.
MOV USKMAX(R1),USKP(R1) ;Make sure we have a good stack pointer
SCHEDU R1,#SERVER,#USRDM,#2 ;Cause the new process to be started, suspended
RTS PC
ALKILL: CLR ALLIVE ;Indicate that the AL interpreter is dead
RTS PC
; BUGS
COMMENT ⊗
DOSTART calls SPAWN, which expects R4 to point to a valid ISB. This
is not always possible, so either SPAWN should be changed, or, more
likely, a special version of SPAWN should be used that sets up an ISB
from scratch, much as is done in AL(3P).
⊗